/*
 * MyGWT User Extensions
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.mygwt.ux.client.action;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.mygwt.ui.client.Style;
import net.mygwt.ui.client.event.BaseEvent;
import net.mygwt.ui.client.widget.Component;
import net.mygwt.ui.client.widget.IconButton;
import net.mygwt.ui.client.widget.Item;

import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.HasText;

/**
 * An action represents the non-UI side of a command which can be triggered by
 * the end user. Actions are typically associated with buttons, menu items, and
 * items in tool bars. When the end user triggers the command via its control,
 * the action's <code>execute</code> method is invoked to do the real work.
 * 
 * @author ggeorg
 */
public class Action implements Command, HasText {

  public static final int BUTTON = 0;
  public static final int TOOLITEM = 1;
  public static final int MENUITEM = 2;
  public static final int ICONBUTTON = 3;

  private String mId;
  private String mText;
  private String mToolTip;
  private String mIconStyle;
  private String mDescription;

  private boolean mEnabled, mChecked, mVisible = true;

  private IActionFactory mActionFactory;

  private List mActionListeners, mItems;

  /**
   * 
   * @param pId
   */
  public Action(String pId) {
    this(pId, null, null, null, null, new DefaultActionFactory());
  }

  /**
   * 
   * @param pId
   * @param pText
   */
  public Action(String pId, String pText) {
    this(pId, pText, null, null, null, new DefaultActionFactory());
  }

  /**
   * 
   * @param pId
   * @param pText
   * @param pToolTipText
   */
  public Action(String pId, String pText, String pToolTipText) {
    this(pId, pText, pToolTipText, null, null, new DefaultActionFactory());
  }

  /**
   * 
   * @param pId
   * @param pText
   * @param pToolTipText
   * @param pIconStyle
   */
  public Action(String pId, String pText, String pToolTipText, String pIconStyle) {
    this(pId, pText, pToolTipText, pIconStyle, null, new DefaultActionFactory());
  }

  /**
   * 
   * @param pId
   * @param pText
   * @param pToolTipText
   * @param pIconStyle
   * @param pDescription
   */
  public Action(String pId, String pText, String pToolTipText, String pIconStyle,
      String pDescription) {
    this(pId, pText, pToolTipText, pIconStyle, pDescription, new DefaultActionFactory());
  }

  /**
   * 
   * @param pId
   * @param pText
   * @param pToolTipText
   * @param pIconStyle
   * @param pDescription
   * @param pActionFactory
   */
  public Action(String pId, String pText, String pToolTipText, String pIconStyle,
      String pDescription, IActionFactory pActionFactory) {
    mId = pId;
    mText = pText;
    mToolTip = pToolTipText;
    mIconStyle = pIconStyle;
    mDescription = pDescription;
    mActionFactory = pActionFactory;
  }

  /**
   * Adds an action listeners that will be invoked to do the real work.
   * 
   * @param pListener the action listener
   */
  public void addActionListener(IActionListener pListener) {
    if (mActionListeners == null) {
      mActionListeners = new ArrayList();
    }
    if (mActionListeners.indexOf(pListener) == -1) {
      mActionListeners.add(pListener);
    }
  }

  /**
   * 
   * @param pType
   * @return
   */
  public Component createItem(int pType) {
    return createItem(pType, Style.NONE);
  }

  /**
   * 
   * @param pType
   * @param pStyle
   * @return
   */
  public Component createItem(int pType, int pStyle) {
    Component item = mActionFactory.createItem(this, pType, pStyle);
    if (item != null) {
      if (mItems == null) {
        mItems = new ArrayList();
      }
      mItems.add(item);
    }
    return item;
  }

  /**
   * Runs this action.
   * 
   * @see com.google.gwt.user.client.Command#execute()
   */
  public void execute() {
    execute(new BaseEvent());
  }

  /**
   * Runs this action, passing the triggering MyGWT event.
   * 
   * @param pEvent the MyGWT event which triggered this action being run
   */
  public void execute(BaseEvent pEvent) {
    if (isEnabled() && mActionListeners != null) {
      for (Iterator i = mActionListeners.iterator(); i.hasNext();) {
        IActionListener l = (IActionListener) i.next();
        try {
          l.handleAction(pEvent);
        } catch (Exception ex) {
          // ignore
        }
      }
    }
  }

  /**
   * @param pEvent
   */
  protected void firePropertyChange(ActionPropertyChangeEvent pEvent) {
    if (mItems == null) {
      return;
    }
    for (Iterator i = mItems.iterator(); i.hasNext();) {
      Object o = i.next();
      if (o instanceof Item) {
        mActionFactory.propertyChange((Item) o, pEvent);
      } else if (o instanceof IconButton) {
        mActionFactory.propertyChange((IconButton) o, pEvent);
      }
    }
  }

  /**
   * Returns an array of all registered <code>IActionListener</code>s.
   * 
   * @return all registered <code>IActionListener</code>s
   */
  public IActionListener[] getActionListeners() {
    return (IActionListener[]) mActionListeners.toArray(new IActionListener[mActionListeners.size()]);
  }

  /**
   * Returns the action's description if it has one. Otherwise it returns
   * <code>getToolTipText()</code>.
   * 
   * @return the action's description or tool tip text if description is
   *         <code>null</code>.
   */
  public String getDescription() {
    if (mDescription != null)
      return mDescription;
    else
      return getToolTip();
  }

  /**
   * Returns the action's icon style text for this action.
   * 
   * @return the icon style text, or <code>null</code> if none
   */
  public String getIconStyle() {
    return mIconStyle;
  }

  /**
   * Returns a unique identifier for this action, or <code>null</code> if it
   * has none.
   * 
   * @return the action id, or <code>null</code> if none
   */
  public String getId() {
    return mId;
  }

  /**
   * Returns the text for this action.
   * 
   * @return the text for this action
   * @see com.google.gwt.user.client.ui.HasText#getText()
   */
  public String getText() {
    return mText;
  }

  /**
   * Returns the tool tip text for this action.
   * 
   * @return the tool tip text, or <code>null</code> if none
   */
  public String getToolTip() {
    return mToolTip;
  }

  /**
   * Returns the checked status of this action.
   * 
   * @return the checked status
   */
  public boolean isChecked() {
    return mChecked;
  }

  /**
   * Returns whether this action is enabled.
   * 
   * @return <code>true</code> if enabled, and <code>false</code> if
   *         disabled
   */
  public boolean isEnabled() {
    return mEnabled;
  }

  /**
   * Returns whether this action's UI elements are visible.
   * 
   * @return <code>true</code> if visible, and <code>false</code> if not
   *         visible
   */
  public boolean isVisible() {
    return mVisible;
  }

  /**
   * Remove an action listeners from the listeners list.
   * 
   * @param pListener the action listener
   */
  public void removeActionListener(IActionListener pListener) {
    if (mActionListeners == null) {
      mActionListeners.remove(pListener);
    }
  }

  /**
   * Remove all action listeners.
   */
  public void removeAllActionListeners() {
    if (mActionListeners == null) {
      mActionListeners.clear();
    }
  }

  /**
   * Sets the checked status of this action.
   * 
   * @param pChecked the new checked status
   */
  public void setChecked(boolean pChecked) {
    Boolean oldValue = new Boolean(pChecked);
    mChecked = pChecked;
    firePropertyChange(new ActionPropertyChangeEvent(this, ActionProperty.CHECKED,
        oldValue, new Boolean(mChecked)));
  }

  /**
   * Sets this action's description. Typically the description is shown as a
   * (longer) help text in the status line.
   * 
   * @param pDescription the description, or <code>null</code> to clear the
   *            description
   */
  public void setDescription(String pDescription) {
    String oldValue = mDescription;
    mDescription = pDescription;
    firePropertyChange(new ActionPropertyChangeEvent(this, ActionProperty.DESCRIPTION,
        oldValue, mDescription));
  }

  /**
   * Sets the enabled state of this action.
   * 
   * @param pEnabled <code>true</code> to enable, and <code>false</code> to
   *            disable
   */
  public void setEnabled(boolean pEnabled) {
    Boolean oldValue = new Boolean(mEnabled);
    mEnabled = pEnabled;
    firePropertyChange(new ActionPropertyChangeEvent(this, ActionProperty.ENABLED,
        oldValue, new Boolean(mEnabled)));
  }

  /**
   * Sets the visible state of this action.
   * 
   * @param pVisible <code>true</code> for visible, and <code>false</code>
   *            to not visible
   */
  public void setVisible(boolean pVisible) {
    Boolean oldValue = new Boolean(mVisible);
    mVisible = pVisible;
    firePropertyChange(new ActionPropertyChangeEvent(this, ActionProperty.VISIBLE,
        oldValue, new Boolean(mVisible)));
  }

  /**
   * Sets the icon style for this action.
   * 
   * @param pIconStyle the icon style text, or <code>null</code> if none
   */
  public void setIconStyle(String pIconStyle) {
    mIconStyle = pIconStyle;
  }

  /**
   * Sets the text for this action.
   * 
   * @return the text, or <code>null</code> if none
   * @see com.google.gwt.user.client.ui.HasText#setText(java.lang.String)
   */
  public void setText(String pText) {
    String oldValue = mText;
    mText = pText;
    firePropertyChange(new ActionPropertyChangeEvent(this, ActionProperty.TEXT, oldValue,
        mText));
  }

  /**
   * Sets the tool tip text for this action.
   * 
   * @param pToolTipText the tool tip text, or <code>null</code> if none
   */
  public void setToolTip(String pToolTipText) {
    String oldValue = mToolTip;
    mToolTip = pToolTipText;
    firePropertyChange(new ActionPropertyChangeEvent(this, ActionProperty.TOOLTIPTEXT,
        oldValue, mToolTip));
  }

}
